#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "scheduler.h"

const int NUM_OF_EXPAND[] = {0, 100};
const int MAX_OF_PHY_MAC = 100000;
const double MAX_SCORE = 200.0;
const double SCORE_OF_BASLINE = 200.0 * 0.6;

typedef struct {
    int n;
    int total_vcpu;
    int total_mem;
    int num_of_ins;
    int *used_vcpu;
    int *used_mem;
    int *id_to_pos;
    int *id_to_vcpu;
    int *id_to_mem;
    Scheduler scheduler;
    FILE* file;
} InteractiveSystem;

void InteractiveSystem_addVM(InteractiveSystem *self, int id, int vcpu, int mem) {
    assert(id == self->id_to_pos[0]);
    self->id_to_pos[id] = -1;
    self->id_to_vcpu[id] = vcpu;
    self->id_to_mem[id] = mem;
}

int InteractiveSystem_create(InteractiveSystem *self, int id, int pos, int vcpu, int mem) {
    if (pos < 0 || pos >= self->n) {
        return 0;
    }
    if (self->used_vcpu[pos] + vcpu > self->total_vcpu || self->used_mem[pos] + mem > self->total_mem) {
        return 0;
    }
    self->id_to_pos[id] = pos;
    self->used_vcpu[pos] += vcpu;
    self->used_mem[pos] += mem;
    return 1;
}

void InteractiveSystem_remove(InteractiveSystem *self, int id) {
    int pos = self->id_to_pos[id];
    int vcpu = self->id_to_vcpu[id];
    int mem = self->id_to_mem[id];
    self->used_vcpu[pos] -= vcpu;
    self->used_mem[pos] -= mem;
}

void InteractiveSystem_init(InteractiveSystem *self, const char *file_name) {
    self->file = fopen(file_name, "r");
    if (self->file == NULL) {
        perror("Failed to open file");
        exit(EXIT_FAILURE);
    }
    fscanf(self->file, "%d %d %d", &self->n, &self->total_vcpu, &self->total_mem);
    fscanf(self->file, "%d", &self->num_of_ins);

    self->used_vcpu = (int *) calloc(MAX_OF_PHY_MAC, sizeof(int));
    self->used_mem = (int *) calloc(MAX_OF_PHY_MAC, sizeof(int));
    self->id_to_pos = (int *) calloc(MAX_OF_PHY_MAC, sizeof(int));
    self->id_to_vcpu = (int *) calloc(MAX_OF_PHY_MAC, sizeof(int));
    self->id_to_mem = (int *) calloc(MAX_OF_PHY_MAC, sizeof(int));

    self->scheduler.init = Scheduler_init;
    self->scheduler.create = Scheduler_create;
    self->scheduler.remove = Scheduler_remove;
    self->scheduler.init(self->n, self->total_vcpu, self->total_mem);
}

int InteractiveSystem_runOnce(InteractiveSystem *self) {
    int id;
    char ins_name[10];
    fscanf(self->file, "%s %d", ins_name, &id);
    int is_success = 1;
    if (ins_name[0] == 'C') {
        int vcpu;
        int mem;
        fscanf(self->file, "%d %d", &vcpu, &mem);
        Pair res = self->scheduler.create(id, vcpu, mem);
        if (res.second < NUM_OF_EXPAND[0] || res.second > NUM_OF_EXPAND[1]) {
            return 0;
        }
        int pos = res.first;
        for (int i = 0; i < res.second; i++) {
            self->n++;
            if (self->n > MAX_OF_PHY_MAC) {
                return 0;
            }
            self->used_vcpu[self->n - 1] = 0;
            self->used_mem[self->n - 1] = 0;
        }
        self->id_to_pos[id] = 0;
        self->id_to_vcpu[id] = vcpu;
        self->id_to_mem[id] = mem;
        is_success = InteractiveSystem_create(self, id, pos, vcpu, mem);
    } else {
        self->scheduler.remove(id);
        InteractiveSystem_remove(self, id);
    }
    return is_success;
}

int InteractiveSystem_getNumOfMachines(InteractiveSystem *self) {
    int is_success = 1;
    for (int i = 0; i < self->num_of_ins; i++) {
        if (!InteractiveSystem_runOnce(self)) {
            is_success = 0;
            break;
        }
    }
    free(self->used_vcpu);
    free(self->used_mem);
    free(self->id_to_pos);
    free(self->id_to_vcpu);
    free(self->id_to_mem);
    fclose(self->file);
    return is_success ? self->n : -1;
}

int main() {
    const char *mode = "GEN_SCH_SCORE";
    if (strstr(mode, "GEN_SCH_SCORE") != NULL) {
        const char *score_path = "../final_score.txt";
        FILE *score_file = fopen(score_path, "w");
        if (score_file == NULL) {
            perror("Failed to open score file");
            exit(EXIT_FAILURE);
        }
        int n_tests = 20;
        double total_score = 0;
        double *score_list = (double *) malloc(n_tests * sizeof(double));
        for (int i = 1; i <= n_tests; i++) {
            fprintf(stderr, "test case #%d: ", i);
            InteractiveSystem inter_sys;
            char file_name[50];
            snprintf(file_name, sizeof(file_name), "../data/%d.in", i);
            InteractiveSystem_init(&inter_sys, file_name);
            int res = InteractiveSystem_getNumOfMachines(&inter_sys);
            snprintf(file_name, sizeof(file_name), "../data/baseline_%d.out", i);
            FILE *baseline_file = fopen(file_name, "r");
            if (baseline_file == NULL) {
                perror("Failed to open baseline file");
                exit(EXIT_FAILURE);
            }
            int baseline;
            fscanf(baseline_file, "%d", &baseline);
            double score = 0.0;
            if (res != -1) {
                //fprintf(stderr, "res = %d\n", res);
                score = 1.0 * baseline / res * SCORE_OF_BASLINE;
                if (score > MAX_SCORE) score = MAX_SCORE;
            }
            score_list[i - 1] = score;
            total_score += score;
            fprintf(stderr, "%.2f\n", score);
            fclose(baseline_file);
        }
        fprintf(stderr, "Final Score: %.2f\n", total_score / n_tests);
        fprintf(score_file, "Final Score: %.2f\n", total_score / n_tests);
        for (int i = 0; i < n_tests; i++) {
            fprintf(score_file, "Score of Test Case #%d: %.2f\n", i + 1, score_list[i]);
        }
        free(score_list);
        fclose(score_file);
    }
    return 0;
}
